Conversation
| return False | ||
| ``` | ||
|
|
||
| ## Code3 (再帰) |
There was a problem hiding this comment.
一応、想像した再帰とは違いました。
再帰下降を見ておいてください。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.5ynll0rwu02h
There was a problem hiding this comment.
再帰降下法はあまり馴染み無かったので実装しました
https://github.com/kitano-kazuki/leetcode/pull/6/changes#diff-cf71cdc6c0d746b654ccddf6a975ccbbea929f8e2d2fee0362ccf6e9f5a3def6
でもあまり自信はないです.
お時間あれば, こちらもレビューしていただけますと幸いです.
| class Solution: | ||
| def isValid(self, s: str) -> bool: | ||
| unclosed_brackets = [] | ||
| brackets_pairs = { |
There was a problem hiding this comment.
命名はopen_to_closeとかにする方が良さそう
https://github.com/kunimomo/leetcode/pull/8/changes#r2771430167
| * 再帰降下法の実装をした. | ||
| * [参考](https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.5ynll0rwu02h) | ||
| * 自然言語で解釈をちゃんとできたらバグなく実装できた. | ||
| * ただ, `nonlocal`な変数として処理をしている`idx`を持つのは初見だと思いつかなさそう. |
There was a problem hiding this comment.
C++ だとリファレンスで idx を与えることもありますね。
カプセル化して、peek (次の文字を見る) consume (次の文字を使う) などの関数を定義するのも一つです。
| while processing_idx < len(s): | ||
| if not check_chunk_valid(): | ||
| return False | ||
| ``` |
There was a problem hiding this comment.
微妙に標準的な再帰下降構文解析とは異なっているように感じます。まあ、別にこれでもいいですが。
標準的なのだと
S -> Chunk S | ε
Chunk -> (S) | {S} | [S]
でしょう。で、parse_s と parse_chunk を両方書けばいいです。
で、最後に return parse_s() and self.pos == len(self.s) します。
There was a problem hiding this comment.
なるほど、先に文脈自由文法で考えるとわかりやすいですね。ありがとうございます。
There was a problem hiding this comment.
LL(1)の実装もしてみました
再帰降下もLL(1)も1文字先を見て, 適用する構文を決定するという仕組みは一緒なんでしょうか.
だとすると, やっていることは再帰降下と一緒なのに実装コストがLL(1)はとても重く感じました.
それぞれの方法の比較として
- LL(1)
- 今回は, left-recrusiveの解消やleft factorizationはしていないが, それをすればどのような文脈自由文法も自然言語として受け取れる.
- 構文解析と字句解析を切り分けられる ← これがいちばんのメリットっぽい??
- 再帰降下
- 人間が解釈して, 先読み文字と適用する構文の関係をコードに組み込む必要がある.
- 実装はLL(1)に比べて楽
- 構文解析と字句解析を同時に行う(?)
There was a problem hiding this comment.
少し違和感があります。
文脈自由文法の真部分集合に LL(1) 文法があって、その伝統的なアルゴリズムに再帰下降とテーブル駆動があります。再帰下降はバックトラックなどをつけてもう少し広いクラスを扱うこともあります。
トークナイザーは、ここでは1文字1トークンになっているが、別にすることは可能です。
テーブル駆動の利点は、コードの自動生成が簡単ということですね。
There was a problem hiding this comment.
テーブル駆動で解析できるものに関して、あらゆる文脈自由文法という表現は不適でLL(1)文法とするべきでしたね。
テーブル駆動の利点がコードの自動生成が簡単というのは考えが及んでいなかったです。ありがとうございます。
| "[" : "]" | ||
| } | ||
| open_brakets = bracket_pairs.keys() | ||
| for i in range(len(s)): |
There was a problem hiding this comment.
s はイテラブル ( str )ですので、for bracket in s と書くと読みやすいと思います。
There was a problem hiding this comment.
確かに今回はiを使ってないので, for bracket in sの方が良さそうです
| continue | ||
| if len(unclosed_open_brackets) == 0: | ||
| return False | ||
| last_open_bracket = unclosed_open_brackets.pop(-1) |
There was a problem hiding this comment.
.pop() はデフォルトがリストの最後ですので、.pop(-1)と等価ですね。
https://docs.python.org/3/tutorial/datastructures.html#more-on-lists
list.pop([i])
Remove the item at the given position in the list, and return it. If no index is specified, a.pop() removes and returns the last item in the list. It raises an IndexError if the list is empty or the index is outside the list range.
There was a problem hiding this comment.
ありがとうございます。
半分手癖なんですけど, pop()が末尾からだよってことを明示したくて毎回pop(-1)にしてます.
| if len(unclosed_open_brackets) > 0: | ||
| return False | ||
| return True |
There was a problem hiding this comment.
ここはreturn not unclosed_open_bracketsと一行でスッキリ書けます。空のリストは falsy (implicit false) なので。
こちらも参考になると思います。
For sequences (strings, lists, tuples), use the fact that empty sequences are false, so if seq: and if not seq: are preferable to if len(seq): and if not len(seq): respectively.
There was a problem hiding this comment.
スタイルガイドがif seqを推奨だったんですね!知らなかったです。ありがとうございます。
| if s[i] in brackets_pairs.keys(): | ||
| unclosed_brackets.append(s[i]) | ||
| else: | ||
| if len(unclosed_brackets) == 0: |
There was a problem hiding this comment.
こちらのコメントをご参照ください。
mamo3gr/arai60#6 (comment)
There was a problem hiding this comment.
ありがとうございます。
if not 変数名
とした方が簡潔でいいですね
No description provided.